/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
    ArkFile,
    ts,
} from 'arkanalyzer';
import { Rule } from '../../Index';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
import { Defects } from '../../Index';
import {
    FileMatcher,
    MatcherCallback,
    MatcherTypes,
} from '../../Index';
import { RuleListUtil } from "../../utils/common/DefectsList";
import { IssueReport } from "../../model/Defects";

interface MessageInfo {
    line: number;
    character: number;
    endCol: number;
    message: string;
}

export class NoUnreachableCheck implements BaseChecker {
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    public metaData: BaseMetaData = {
        severity: 2,
        ruleDocPath: 'docs/no-unreachable-check.md',
        description: 'Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements',
    };

    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };

    public registerMatchers(): MatcherCallback[] {
        const matchFileCb: MatcherCallback = {
            matcher: this.fileMatcher,
            callback: this.check
        }
        return [matchFileCb];
    }
    private checkUnreachableCode(code: string): MessageInfo[] {
        const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
        const errors: MessageInfo[] = [];
        function visit(node: ts.Node) {
            if (!node) {
                return;
            }

            // 检查 return、throw、break、continue 之后的不可达代码
            if (ts.isReturnStatement(node) || ts.isThrowStatement(node) || ts.isBreakStatement(node) || ts.isContinueStatement(node)) {
                const nextStatement = findNextStatement(node);
                if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                    const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                    errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                }
            }

            // 检查无限循环语句（例如 for(;;) {}）之后的不可达代码
            if (ts.isForStatement(node) && !node.condition) {
                const nextStatement = findNextStatement(node);
                if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                    const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                    errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                }
            }

            // 检查 switch 语句中的不可达代码
            if (ts.isSwitchStatement(node)) {
                let isSwitchUnreachable = true;
                for (const caseClause of node.caseBlock.clauses) {
                    let isCaseUnreachable = false;

                    for (let i = 0; i < caseClause.statements.length; i++) {
                        const currentStatement = caseClause.statements[i];
                        // 如果已经遇到控制流终止语句，则其后的代码都是不可达的
                        if (isCaseUnreachable) {
                            const { line, character } = sourceFile.getLineAndCharacterOfPosition(currentStatement.getStart());
                            errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                        }

                        // 检查当前语句是否终止了控制流
                        if (currentStatement && isControlFlowTerminated(currentStatement)) {
                            isCaseUnreachable = true;
                        } else {
                            // 如果某个 case 分支没有终止控制流，则 switch 之后的代码可能可达
                            isSwitchUnreachable = false;
                        }
                    }
                }

                // 如果 switch 整体不可达，则检查 switch 之后的代码
                if (isSwitchUnreachable) {
                    const nextStatement = findNextStatement(node);
                    if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                        const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                        errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                    }
                }
            }

            // 检查 if-else 语句中的不可达代码
            if (ts.isIfStatement(node)) {
                const thenTerminated = isControlFlowTerminated(node.thenStatement);
                const elseTerminated = node.elseStatement ? isControlFlowTerminated(node.elseStatement) : false;

                // 如果 if 和 else 都终止了控制流，则 if-else 之后的代码不可达
                if (thenTerminated && elseTerminated) {
                    const nextStatement = findNextStatement(node);
                    if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                        const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                        errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                    }
                }
            }

            // 检查函数体中所有可能的执行路径是否都中断了
            if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node)) {
                const body = node.body;

                // 处理 body 为 undefined 的情况（例如函数声明没有函数体）
                if (!body) {
                    return;
                }

                // 处理箭头函数的简写形式（例如 `() => expr`）
                if (!ts.isBlock(body)) {
                    // 如果函数体是表达式，则不需要检查不可达代码
                    return;
                }

                // 检查函数体中所有可能的执行路径是否都中断了
                for (let i = 0; i < body.statements.length; i++) {
                    const currentStatement = body.statements[i];
                    if (currentStatement && isControlFlowTerminated(currentStatement)) {
                        // 如果当前语句终止了控制流，检查下一条语句是否不可达
                        const nextStatement = i < body.statements.length - 1 ? body.statements[i + 1] : undefined;
                        if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                            const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                            errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                        }
                    }
                }
            }

            // 检查类声明
            if (ts.isClassDeclaration(node)) {
                const classMembers = node.members;

                // 检查是否有构造函数
                const constructorE = classMembers.find(member => ts.isConstructorDeclaration(member));
                if (constructorE) {
                    // 检查构造函数是否调用了 super()
                    const hasSuperCall = hasSuperCallInConstructor(constructorE);
                    if (!hasSuperCall) {
                        // 如果构造函数没有调用 super()，则实例字段是不可达的
                        for (const member of classMembers) {
                            if (ts.isPropertyDeclaration(member) && !member.modifiers?.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)) {
                                const { line, character } = sourceFile.getLineAndCharacterOfPosition(member.getStart());
                                errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                            }
                        }
                    }
                }
            }

            // 检查全局作用域中的不可达代码
            if (ts.isSourceFile(node) && !ts.isClassDeclaration(node)) {
                let isUnreachable = false; // 标记是否已经遇到控制流终止语句
                for (let i = 0; i < node.statements.length; i++) {
                    const currentStatement = node.statements[i];
                    // 如果已经遇到控制流终止语句，则其后的非函数/方法声明代码都是不可达的
                    if (isUnreachable) {
                        if (!ts.isFunctionDeclaration(currentStatement) && !ts.isMethodDeclaration(currentStatement) && !ts.isClassDeclaration(currentStatement) && !ts.isThrowStatement(currentStatement)) {
                            const { line, character } = sourceFile.getLineAndCharacterOfPosition(currentStatement.getStart());
                            errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                        }
                    }

                    // 检查当前语句是否终止了控制流
                    if (currentStatement && isControlFlowTerminated(currentStatement)) {
                        isUnreachable = true;
                    }
                }
            }

            // 新增循环体内部检查（处理 while/for/do-while）
            if (ts.isWhileStatement(node) || ts.isForStatement(node) || ts.isDoStatement(node)) {
                const body = ts.isForStatement(node) ? node.statement : node.statement;
                if (ts.isBlock(body)) {
                    let isUnreachable = false;
                    for (const statement of body.statements) {
                        // 标记不可达代码
                        if (isUnreachable) {
                            const { line, character } = sourceFile.getLineAndCharacterOfPosition(statement.getStart());
                            errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                        }
                        // 判断是否中断循环控制流
                        if (isBreakOrContinue(statement) || isControlFlowTerminated(statement)) {
                            isUnreachable = true;
                        }
                    }
                }
            }

            // 检查 try-catch-finally 块中的不可达代码
            if (ts.isTryStatement(node)) {
                const tryBlock = node.tryBlock;
                const catchClause = node.catchClause;
                const finallyBlock = node.finallyBlock;

                // 检查 try 块中的 return 语句是否会导致 catch 块中的代码不可达
                if (tryBlock && isControlFlowTerminated(tryBlock)) {
                    // 标记 catch 块中的代码为不可达
                    if (catchClause && catchClause.block) {
                        const { line, character } = sourceFile.getLineAndCharacterOfPosition(catchClause.block.getStart());
                        errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                    }

                    // 检查 try 块之后的代码是否不可达
                    const nextStatement = findNextStatement(node);
                    if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                        const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                        errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                    }
                }

                // 如果 finally 块存在，则 finally 块中的代码总是会执行，不应标记为不可达
                if (finallyBlock) {
                    // 检查 finally 块中的代码是否会导致之后的代码不可达
                    if (isControlFlowTerminated(finallyBlock)) {
                        const nextStatement = findNextStatement(node);
                        if (nextStatement && !ts.isFunctionDeclaration(nextStatement)) {
                            const { line, character } = sourceFile.getLineAndCharacterOfPosition(nextStatement.getStart());
                            errors.push({ line: line + 1, character: character + 1, endCol: character + 1 + node.getText().length, message: 'Unreachable code' });
                        }
                    }
                }
            }

            ts.forEachChild(node, visit);
        }
        // 查找当前语句之后的下一条语句
        function findNextStatement(node: ts.Node): ts.Statement | undefined {
            const parent = node.parent;
            if (ts.isBlock(parent)) {
                const index = parent.statements.indexOf(node as ts.Statement);
                if (index < parent.statements.length - 1) {
                    return parent.statements[index + 1];
                }
            }
            return undefined;
        }
        // 判断一个语句是否会导致控制流中断
        function isControlFlowTerminated(node: ts.Node | undefined): boolean {
            if (!node) {
                return false; // 如果 node 为 undefined 或 null，直接返回 false
            }

            // 处理 return、throw、break、continue 语句
            if (
                ts.isReturnStatement(node) ||
                ts.isThrowStatement(node) ||
                ts.isBreakStatement(node) ||
                ts.isContinueStatement(node)
            ) {
                return true;
            }

            // 处理无限循环语句
            if (ts.isForStatement(node) && !node.condition) {
                return true;
            }

            // 处理 if 语句
            if (ts.isIfStatement(node)) {
                // 检查 then 分支是否终止控制流
                const thenTerminated = isControlFlowTerminated(node.thenStatement);

                // 检查 else 分支是否终止控制流
                const elseTerminated = node.elseStatement ? isControlFlowTerminated(node.elseStatement) : false;

                // 只有当 then 和 else 都终止控制流时，整个 if 语句才终止控制流
                return thenTerminated && elseTerminated;
            }

            // 新增对 break/continue 的判断
            if (ts.isBreakStatement(node) || ts.isContinueStatement(node)) {
                return true;
            }

            // 处理 try-finally 语句
            if (ts.isTryStatement(node)) {
                // 检查 try 块是否终止控制流
                const tryTerminated = isControlFlowTerminated(node.tryBlock);

                // 检查 finally 块是否终止控制流
                const finallyTerminated = node.finallyBlock
                    ? isControlFlowTerminated(node.finallyBlock)
                    : false;

                // 如果 try 或 finally 块终止了控制流，则整个 try-finally 语句终止控制流
                return tryTerminated || finallyTerminated;
            }

            // 处理 do-while 语句
            if (ts.isDoStatement(node)) {
                // 检查 do 块是否终止控制流
                return isControlFlowTerminated(node.statement);
            }

            if (ts.isWhileStatement(node)) {
                // 如果 while 条件为 true，并且块中没有 break 或 return，则其后的代码是不可达的
                if (node.expression.kind === ts.SyntaxKind.TrueKeyword) {
                    // 检查 while 块中是否有 break 或 return
                    if (ts.isBlock(node.statement)) {
                        for (const statement of node.statement.statements) {
                            if (isControlFlowTerminated(statement)) {
                                return false; // 如果有 break 或 return，则 while 之后的代码可能可达
                            }
                        }
                    }
                    return true; // 如果没有 break 或 return，则 while 之后的代码不可达
                }
                return false; // 如果 while 条件不是 true，则 while 之后的代码可能可达
            }

            // 处理 switch 语句
            if (ts.isSwitchStatement(node)) {
                // 检查 switch 语句中的每一个 case 和 default 分支
                for (const caseClause of node.caseBlock.clauses) {
                    for (const statement of caseClause.statements) {
                        if (isControlFlowTerminated(statement)) {
                            // 如果某个分支终止了控制流，则整个 switch 语句终止控制流
                            return true;
                        }
                    }
                }
                return false; // 如果没有任何分支终止控制流，则 switch 语句不会终止控制流
            }

            // 处理块语句（例如 {} 包裹的代码块）
            if (ts.isBlock(node)) {
                // 检查块中的每一条语句是否终止控制流
                for (const statement of node.statements) {
                    if (isControlFlowTerminated(statement)) {
                        return true;
                    }
                }
                return false;
            }
            // 处理其他语句（例如表达式语句、变量声明等）
            return false;
        }
        // 检查构造函数中是否调用了 super(),.../
        function hasSuperCallInConstructor(constructorEle: ts.ClassElement): boolean {
            let hasSuperCall = false;
            function visit(node: ts.Node) {
                if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.SuperKeyword) {
                    hasSuperCall = true;
                }
                ts.forEachChild(node, visit);
            }

            if (constructorEle) {
                ts.forEachChild(constructorEle, visit);
            }

            return hasSuperCall;
        }
        // 添加辅助判断函数
        function isBreakOrContinue(node: ts.Node): boolean {
            return ts.isBreakStatement(node) || ts.isContinueStatement(node);
        }

        visit(sourceFile);
        return errors;
    }
    public check = (targetField: ArkFile) => {
        const severity = this.rule.alert ?? this.metaData.severity;
        const filePath = targetField.getFilePath();
        let code = targetField.getCode();
        const myInvalidPositions = this.checkUnreachableCode(code);
        myInvalidPositions.forEach(pos => {
            this.addIssueReport(filePath, pos, severity)
        });
    }
    private addIssueReport(filePath: string, pos: MessageInfo, severity: number) {
        let defect = new Defects(pos.line, pos.character, pos.endCol, pos.message, severity, this.rule.ruleId,
            filePath, this.metaData.ruleDocPath, true, false, false);

        this.issues.push(new IssueReport(defect, undefined));
        RuleListUtil.push(defect);
    }
}